/** @file ByteString.h
	@brief Definition of ByteString_t.
	
	There is defined class ByteString_t.
*/

#ifndef __byte_string_h__
#define __byte_string_h__

#include "exception.h"
#include "Storage.h"
				
/////////////////////////////////////////////////
/// @brief class ByteString_t.
/////////////////////////////////////////////////	
class ByteString_t : public StorageObject_t {
		/// Content.
		char * string;

		/// Length of string content.
		size_t string_len;

		/// Preallocated size of string.
		size_t string_size;

		/// @brief Reallocate string enougth for specified number of bytes.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		void realloc(size_t _size) throw( std::bad_alloc ) {
				if( string_size < npos - string_size && _size < 2 * string_size )
						_size = 2 * string_size;
			
				char * new_string = new char[ _size ];
			
				memcpy(new_string, string, string_len);
			
				delete [] string;
				
				string = new_string;
				string_size = _size;
		}

public:
		/// @brief Default constructor creates empty ByteString.
		ByteString_t() throw() : string( NULL ), string_len( 0 ), string_size( 0 ) {
		}

		/// @brief Construct ByteString with copy of _str value.
		///
		/// @exception std::bad_alloc from new
		ByteString_t(const ByteString_t & _str) throw( std::bad_alloc ) : string_len( _str.string_len ), string_size( _str.string_len ) {
				string = new char[ string_size ];
				memcpy(string, _str.string, string_len);
		}

		/// @brief Construct ByteString initialized by character array.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t(const char * _array, size_t _n) throw( std::bad_alloc, std::out_of_range ) : string_len( _n ), string_size( _n ) {
				if( string_size >= npos )
						throw std::out_of_range( "ByteString_t::ByteString_t(const char *, size_t)" );
				
				string = new char[ string_size ];
				memcpy(string, _array, string_len);
		}
		
		/// @brief Construct ByteString with value of C-style string.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t(const char * _cstr) throw( std::bad_alloc, std::out_of_range ) {
				string_size = string_len = strlen(_cstr);
				
				if( string_size >= npos )
						throw std::out_of_range( "ByteString_t::ByteString_t(const char *)" );
				
				string = new char[ string_size ];
				memcpy(string, _cstr, string_len);
		}
		
		/// @brief Construct ByteString as single character.
		///
		/// @exception std::bad_alloc from new
		ByteString_t(char _c) throw( std::bad_alloc ) : string_len( 1 ), string_size( 1 ) {
				string = new char[ string_size ];
				string[ 0 ] = _c;
		}
		
		/// @brief Destroy ByteString instance.
		~ByteString_t() throw() {
				delete [] string;
		}
		
		/// @brief Append a ByteString.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t & append(const ByteString_t & _str) throw( std::bad_alloc, std::out_of_range ) {
				if( _str.string_len >= string_size - string_len ) {
						if( string_len >= npos - _str.string_len )
								throw std::out_of_range("ByteString_t::append(const ByteString &)");				
						realloc(string_len + _str.string_len);
				}
				
				memcpy(string + string_len, _str.string, _str.string_len);
			
				string_len += _str.string_len;
			
				return *this;
		}

		/// @brief Append a byte array.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t & append(const char * _array, size_t _n) throw( std::bad_alloc, std::out_of_range ) {
				if( _n >= string_size - string_len ) {
						if( _n >= npos - string_len )
								throw std::out_of_range("ByteString_t::append(const char *, size_t)");
						realloc(string_len + _n);
				}

				memcpy(string + string_len, _array, _n);
			
				string_len += _n;
				
				return *this;
		}

		/// @brief Append a c-style string.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t & append(const char * _cstr) throw( std::bad_alloc, std::out_of_range ) {
				const size_t n = strlen(_cstr);
				if( n >= string_size - string_len ) {
						if( n >= npos - string_len )
								throw std::out_of_range("ByteString_t::append(const char *)");
						realloc(string_len + n);
				}

				memcpy(string + string_len, _cstr, n);
			
				string_len += n;
				
				return *this;
		}
		
		/// @brief Append a character.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		ByteString_t & append(char _c) throw( std::bad_alloc, std::out_of_range ) {
				if( string_size == string_len ) {
						if( npos - 1 == string_len )
								throw std::out_of_range("ByteString_t::append(char)");
						realloc(string_len + 1);
				}

				string[string_len++] = _c;
				
				return *this;
		}
		
		/// @brief Provides access to the data contained in the string.
		///
		/// @exception std::out_of_range _n point out of array
		char & at(size_t _n) throw( std::out_of_range ) {
				if( _n >= string_len )
						throw std::out_of_range("ByteString_t::at(size_t)");
				return string[_n];
		}
		
		/// @brief Provides read-only access to the data contained in the string.
		///
		/// throw std::out_of_range _n point out of array
		const char & at(size_t _n) const throw( std::out_of_range ) {
				if( _n >= string_len )
						throw std::out_of_range("ByteString_t::at()");
				return string[_n];
		}

		/// @brief Find first character _c.
		///
		/// @return index of first character _c or length().
		size_t find_chr(char _c) const throw() {
				size_t i = 0;
				while( string_len != i && _c != string[i] )
						++i;
				return i;
		}

		/// @brief Find last character _c.
		///
		/// @return index of last character _c or -1;
		size_t find_rchr(char _c) const throw() {
				size_t i = string_len - 1;
				while( (size_t)-1 != i && _c != string[i] )
						--i;
				return i;
		}
		
		/// @brief Erase this package content.
		void clear() throw() {
				delete [] string;
				string = NULL;
				string_len = 0;
				string_size = 0;
		}

		/// @brief Return pointer to contents.
		char * array() throw() {
				return string;
		}

		/// @brief Return pointer to read-only contents.
		const char * array() const throw() {
				return string;
		}

		/// @brief Return length of contents.
		size_t length() const throw() {
				return string_len;
		}

		/// @brief Resize ByteString to specified number of bytes.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		void resize(size_t _len, char _c = 0) throw( std::bad_alloc, std::out_of_range ) {
				if( string_len >= _len ) 
						string_len = _len;

				else {	
						if( _len >= npos )
								throw std::out_of_range("ByteString_t::resize(size_t, char = 0)");

						if( string_size < _len )
								realloc(_len);

						memset(string + string_len, _c, _len - string_len);
						
						string_len = _len;
				}
		}
		
		/// @brief Attempt to preallocate enougth memory for specified number of bytes.
		///
		/// @exception std::bad_alloc from new
		/// @exception std::out_of_range overful over npos
		void reserv(size_t _size) throw( std::bad_alloc, std::out_of_range ) {
				if( _size > string_size ) {
						if( _size >= npos )
								throw std::out_of_range("ByteString_t::reserv(size_t)");
						realloc(_size);
				}
		}
		
		/// Maximal length of package content.
		static const size_t npos = static_cast<size_t>(-1) / 2;

		/// StorageObject_t interface.
		/// Set object state.
		/// @exception std::invalid_argument there is wrong state.
		/// @exception std::bad_alloc from new.
		void set_state(const char * _state, size_t _size) throw( std::bad_alloc, std::invalid_argument ) {
				reserv(_size);
				memcpy(string, _state, _size);
				string_len = _size;
		}

		/// Get object state.
		/// @exception std::bad_alloc from new.
		virtual void get_state(char * & _state, size_t & _size) const throw( std::bad_alloc ) {
				_state = new char[ string_len ];
				memcpy(_state, string, string_len);
				_size = string_len;
		}
};

#endif //__byte_string_h__
